<template>
  <div class="app-container">
    <el-button type="primary" @click="handleAddRole">New Role</el-button>

    <el-table :data="rolesList" style="width: 100%;margin-top:30px;" border>
      <el-table-column align="center" label="Role key" width="220">
        <template #default="scope">
          {{ scope.row.key }}
        </template>
      </el-table-column>
      <el-table-column align="center" label="Role Name" width="220">
        <template #default="scope">
          {{ scope.row.name }}
        </template>
      </el-table-column>
      <el-table-column align="header-center" label="Description">
        <template #default="scope">
          {{ scope.row.description }}
        </template>
      </el-table-column>
      <el-table-column align="center" label="Opeartions">
        <template #default="scope">
          <el-button type="primary" size="small" @click="handleEdit(scope)">Edite</el-button>
          <el-button type="danger" size="small" @click="handleDelete(scope)">Delete</el-button>
        </template>
      </el-table-column>
    </el-table>

    <el-dialog v-model="dialogVisible" :title="dialogType==='edit'?'Edit Role':'New Role'">
      <el-form :model="role" label-width="80px" label-position="left">
        <el-form-item label="Name">
          <el-input v-model="role.name" placeholder="Role Name" />
        </el-form-item>
        <el-form-item label="Desc">
          <el-input
            v-model="role.description"
            :autosize="{ minRows: 2, maxRows: 4 }"
            type="textarea"
            placeholder="Role Description"
          />
        </el-form-item>
        <el-form-item label="Menus">
          <el-tree
            ref="tree"
            :check-strictly="checkStrictly"
            :data="routesData"
            :props="defaultProps"
            show-checkbox
            node-key="path"
            class="permission-tree"
          />
        </el-form-item>
      </el-form>
      <div style="text-align: right;">
        <el-button type="danger" @click="dialogVisible=false">Cancel</el-button>
        <el-button type="primary" @click="confirmRole">Confirm</el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
export default {
  name: 'RolePermission'
}
</script>

<script setup>
import { ref, computed, nextTick } from 'vue'
import { ElMessageBox, ElMessage, ElNotification } from 'element-plus'
import path from 'path-browserify'
import { deepClone } from '@/utils'
import { getRoutes, getRoles, addRole, deleteRole, updateRole } from '@/api/role'

const defaultRole = {
  key: '',
  name: '',
  description: '',
  routes: []
}

const role = ref(Object.assign({}, defaultRole))
const routes = ref([])
const rolesList = ref([])
const dialogVisible = ref(false)
const dialogType = ref('new')
const checkStrictly = ref(false)
const defaultProps = ref({
  children: 'children',
  label: 'title'
})
const tree = ref(null)

const routesData = computed(() => routes.value)
const serviceRoutes = {}

const generateArr = routes => {
  let data = []
  routes.forEach(route => {
    data.push(route)
    if (route.children) {
      const temp = generateArr(route.children)
      if (temp.length > 0) {
        data = [...data, ...temp]
      }
    }
  })

  return data
}

const handleAddRole = () => {
  role.value = Object.assign({}, defaultRole)
  if (tree.value) {
    tree.value.setCheckedNodes([])
  }
  dialogType.value = 'new'
  dialogVisible.value = true
}

const handleEdit = scope => {
  dialogType.value = 'edit'
  dialogVisible.value = true
  checkStrictly.value = true
  role.value = deepClone(scope.row)
  nextTick(() => {
    const routes = generateRoutes(role.value.routes)
    tree.value.setCheckedNodes(generateArr(routes))

    checkStrictly.value = false
  })
}

const handleDelete = ({ $index, row }) => {
  ElMessageBox.confirm(
    'Confirm to remove the role?',
    'Warning',
    {
      confirmButtonText: 'Confirm',
      cancelButtonText: 'Cancel',
      type: 'warning'
    }
  ).then(async() => {
    await deleteRole(row.key)
    rolesList.value.splice($index, 1)
    ElMessage({
      type: 'success',
      message: 'Delete succed!'
    }).catch(err => {
      console.error(err)
    })
  }).catch(err => {
    console.error(err)
  })
}

const generateTree = (routes, basePath = '/', checkedKeys) => {
  const res = []

  for (const route of routes) {
    const routePath = path.resolve(basePath, route.path)

    // recursive child routes
    if (route.children) {
      route.children = generateTree(route.children, routePath, checkedKeys)
    }

    if (checkedKeys.includes(routePath) || (route.children && route.children.length >= 1)) {
      res.push(route)
    }
  }

  return res
}

const confirmRole = async() => {
  const isEdit = dialogType.value === 'edit'

  const checkedKeys = tree.value.getCheckedKeys()
  role.value.routes = generateTree(deepClone(serviceRoutes.value), '/', checkedKeys)
  if (isEdit) {
    await updateRole(role.value.key, role.value)
    for (let index = 0; index < rolesList.value.length; index++) {
      if (rolesList.value[index].key === role.value.key) {
        rolesList.value.splice(index, 1, Object.assign({}, role.value))
        break
      }
    }
  } else {
    const { data } = await addRole(role.value)
    role.value.key = data.key
    rolesList.value.push(role.value)
  }

  const { description, key, name } = role.value
  dialogVisible.value = false
  ElNotification({
    title: 'Success',
    dangerouslyUseHTMLString: true,
    message: `<div>Role Key: ${key}</div><div>Role Name: ${name}</div><div>Description: ${description}</div>`,
    type: 'success'
  })
}

const onlyOneShowingChild1 = (children = [], parent) => {
  let onlyOneChild = null
  const showingChildren = children.filter(item => !item.hidden)

  // When there is only one child route, the child route is displayed by default
  if (showingChildren.length === 1) {
    onlyOneChild = showingChildren[0]
    onlyOneChild.path = path.resolve(parent.path, onlyOneChild.path)
    return onlyOneChild
  }

  // Show parent if there are no child route to display
  if (showingChildren.length === 0) {
    onlyOneChild = { ...parent, path: '', noShowingChildren: true }
    return onlyOneChild
  }

  return false
}

const generateRoutes = (routes, basePath = '/') => {
  const res = []

  for (let route of routes) {
    if (route.hidden) {
      continue
    }

    const onlyOneShowingChild = onlyOneShowingChild1(route.children, route)

    if (route.children && onlyOneShowingChild && !route.alwaysShow) {
      route = onlyOneShowingChild
    }

    const data = {
      path: path.resolve(basePath, route.path),
      title: route.meta && route.meta.title
    }

    // recursive child routes
    if (route.children) {
      data.children = generateRoutes(route.children, data.path)
    }
    res.push(data)
  }

  return res
}

const getRoutes1 = async() => {
  const res = await getRoutes()
  serviceRoutes.value = res.data
  routes.value = generateRoutes(res.data)
}

const getRoles1 = async() => {
  const res = await getRoles()
  rolesList.value = res.data
}

getRoutes1()
getRoles1()

</script>

<style lang="scss" scoped>
.app-container {
  .roles-table {
    margin-top: 30px;
  }
  .permission-tree {
    margin-bottom: 30px;
  }
}
</style>
